﻿using ICMS.PluginInterfaces;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using commonImportPlugin.Config;
using commonImportPlugin.Configs.PbocFile;
using commonImportPlugin.Common;

namespace commonImportPlugin.FilesCore.PbocFile
{
    public class PbocBase 
    {
        CommonUtil commonUtil = CommonUtil.GetInstance();//公共类处理核心

        #region 获取记录数
        /// <summary>
        /// 功能：获取一个金融文件中的数据条数
        /// 概述：根据正则表达式匹配到表示数据条数的字节，然后将该字节转为int      
        /// </summary>
        /// <param name="fs">文件流指针</param>
        /// <param name="config">配置类
        /// config.RecordCount记录数。=0则如下参数有效,>0 则表示给固定值RecordCount.=-1表示.无法直接读取记录数
        /// config.isRevers 长度字节是否逆序
        /// config.getRecordCountRegex 匹配找到表示长度字节的正则表达式
        /// config.getRecordCountSpaceSize 读取前多少字节用来匹配
        /// config.getRecordCountLength  表示长度字节的参数(1-4表示第1到4字节为长度）
        /// config.getRecordCountStepAdjust 微调指针位置参数-1表后移一位
        /// </param>
        /// <returns>数据条数</returns>
        public int GetRecordCounts(Stream fs, PbocFileConfig config)
        {            
            string errorMessages = null;//错误信息
            long fsPosition = 0;    //当前fs文件流位置
            int RecordCount = 0;    //记录数

            string[] splitLengthArray = null; //分割长度参数的数组
            int lengthStart = 0;      //长度开始位置（1开始)
            int lengthEnd = 0;        //长度结束位置（1开始)
            int LengthBytes = 0;     //长度的占多少字节

            byte[] buffer = null;         //存放要匹配的byte
            byte[] lengthBuffer = null;   //存放长度的byte[]
            string allstring = null;      //用来正则匹配的字符

            try
            {
                errorMessages = fs == null ? "执行函数getRecordCounts出现fs==null 请先打开文件！" : errorMessages;
                //不是0则直接返回
                if (string.IsNullOrEmpty(config.getRecordCountRegex))
                    return config.getRecordCountConstantValue;               
                //获取长度参数设置
                splitLengthArray = config.getRecordLength.Split(',');
                lengthStart = int.Parse(splitLengthArray[0]);
                lengthEnd = int.Parse(splitLengthArray[1]);
                LengthBytes = lengthEnd - lengthStart + 1;

                //开辟空间
                buffer = new byte[config.getRecordCountSpaceSize];
                lengthBuffer = new byte[LengthBytes];

                //记录初始文件流位置
                fsPosition = fs.Position;

                //获取指定位数的字符串
                fs.Read(buffer, 0, config.getRecordCountSpaceSize);
                allstring = BitConverter.ToString(buffer).Replace("-", "");

                //匹配找到
                Match match = Regex.Match(allstring, config.getRecordCountRegex);
                if (match.Success)
                {
                    int location = match.Index / 2;
                    fs.Position = location + lengthStart - 1;
                    fs.Read(lengthBuffer, 0, LengthBytes);
                    RecordCount = ByteArrayToInt(lengthBuffer, config.isRevers);
                    return RecordCount;
                }
                else
                    return 0;
            }
            catch (Exception)
            {
                throw new Exception("执行函数getRecordCounts出错！");
            }
            finally
            {
                //文件流指针复位
                fs.Position = fsPosition;
            }
        }
        #endregion

        #region 设置位置
        /// <summary>
        /// 功能：根据配置设定fs的position位置
        /// 概述：根据正则表达式匹配到指定位置，再通过微调参数setPositionStepAdjust设置
        /// </summary>
        /// <param name="fs">fs文件流</param>
        /// <param name="config">
        /// config.setPositionRegex 匹配到指定位置的正则表达式
        /// config.setPositionSpaceSize 读取前多少字节用来匹配
        /// config.setPositionStepAdjust 微调指针位置参数-1表后移一位
        /// </param>
        /// <returns>-1 设置失败，其他则表示成功</returns>
        public int setPosition(Stream fs, PbocFileConfig config)
        {
            try
            {
                int location = 0;//文件流位置
                byte[] buffer = null;//存放读取前多少字节
                string stringBuffer = null;//存放buffer转成的字符串

                if (fs == null)
                    return -1;
                if (string.IsNullOrEmpty(config.setPositionRegex))
                {
                    fs.Position = config.setPositionConstantValue;
                    return (int)fs.Position;
                }
                //读取前setPositionSpaceSize字节
                buffer = new byte[config.setPositionSpaceSize];
                fs.Read(buffer, 0, config.setPositionSpaceSize);
                stringBuffer = BitConverter.ToString(buffer).Replace("-", "");

                //匹配
                Match match = Regex.Match(stringBuffer, config.setPositionRegex);
                if (match.Success)
                {
                    location = match.Index / 2;
                    fs.Position = location + config.setPositionStepAdjust;
                    return location;
                }
                else
                    return -1;
            }
            catch (Exception)
            {
                throw new Exception("执行函数setPosition出错！");
            }
        }
        #endregion

        #region 获取DGI数据
        /// <summary>
        /// 根据配置获取每一条金融数据（普通模式)
        /// </summary>
        /// <param name="fs">文件流指针</param>
        /// <param name="config">
        /// config.icDataFormat 读取的IC数据的格式，0：二进制格式，1：转码后格式
        /// config.icRules      读取icData的参数          
        /// config.trackFormat  读取的磁条数据的格式，0：二进制格式，1：转码后格式
        /// config.trackSeparators 分割磁条数据的分隔符(1个或多个)
        /// config.trackRules  读取磁条数据的参数规则
        /// </param>
        /// <returns>一条金融数据(有磁条则含磁条）</returns>
        public ICData GetDgiDataGeneralMode(Stream fs, PbocFileConfig config)
        {
            //一条金融数据
            string pbocData = null;
            //一条ic数据
            ICData pbocIcData = new ICData();
            //5A的值
            string fiveAData = null;
            string[] track123Datas = null;//存放1,2，3磁道的数据

            try
            {
                //遍历每个参数规则
                foreach (string icRule in config.icRules)
                {
                    Rule rule = GetRule(icRule);
                    #region  ======根据配置截取=======
                    switch (rule.DgiTag)
                    {
                        case "over"://表示跳过的字节
                            {
                                if (rule.mode == 2)
                                    fs.Position += rule.overStepSize;
                                else
                                    fs.Position += GetLength(fs, rule.lengthStart, rule.lengthEnd, rule.lengthConstantValue, config.isRevers) + rule.lengthEnd;
                            }
                            break;
                        case "track"://表示磁条的字节
                            {
                                string trackData=null;
                                string originTrackDatas = this.GetString(fs, rule, config.isRevers, config.trackFormat, false);

                                if(originTrackDatas!=null)
                                  trackData = GetTrackData(originTrackDatas.TrimEnd('\0'), config);

                                pbocIcData.Track1 = trackData;
                            }
                            break;
                        case "bankCardId":  //银行卡号
                            {
                                string originBankCardId = GetMatchMessges(fs, config.bankCardIdRules, config.bankCardIdFormat, config.isRevers);
                                if (originBankCardId != null)
                                   pbocIcData.BankCardID = originBankCardId.Trim().Replace(" ", "");
                            }
                            break;
                        case "personId":    //证件号
                            {
                                string originPersonId = GetMatchMessges(fs, config.personIdRules, config.personIdFormat, config.isRevers);
                                if (originPersonId != null)
                                    pbocIcData.PersonID = originPersonId.Trim().Replace(" ", "");
                            }
                            break;          
                        case "5A":        //银行卡号
                            {
                                string origin5A = GetMatchMessges(fs, config.fiveARules, config.fiveAFormat, config.isRevers);
                                fiveAData = string.Format("5A|{0}", origin5A);              
                            }
                            break;
                        case "track1":        //1磁道数据
                            {
                                track123Datas = track123Datas == null ? new string[3] : track123Datas;
                                string track1Data = this.GetString(fs, rule, config.isRevers, config.icDataFormat, false).Trim();
                                track123Datas[0] = track1Data;//1磁道数据
                            }
                            break;
                        case "track2":        //2磁道数据
                            {
                                track123Datas = track123Datas == null ? new string[3] : track123Datas;
                                string track2Data = this.GetString(fs, rule, config.isRevers, config.icDataFormat, false).Trim();
                                track123Datas[1] = track2Data;//2磁道数据
                            }
                            break;
                        case "track3":        //3磁道数据
                            {
                                track123Datas = track123Datas == null ? new string[3] : track123Datas;
                                string track3Data = this.GetString(fs, rule, config.isRevers, config.icDataFormat, false).Trim();
                                track123Datas[2] = track3Data;//3磁道数据
                            }
                            break;
                        default:    //表示IC数据的字节
                            {
                                string dgiData = null;
                                if(!string.IsNullOrEmpty(rule.DgiTag))
                                {
                                    dgiData = this.GetString(fs, rule, config.isRevers, config.icDataFormat, false).Trim();
                                    pbocData += string.Format("{0}|{1}|", rule.DgiTag.Trim(), dgiData);
                                }
                                else
                                {
                                    pbocData = this.GetString(fs, rule, config.isRevers, config.icDataFormat, false).Trim();
                                }
                            }
                            break;
                    }
                    #endregion
                }
                //磁条数据
                if (track123Datas != null)
                    pbocIcData.Track1 = string.Format("%{0}?;{1}?_;{2}?", track123Datas[0], track123Datas[1], track123Datas[2]);

                pbocIcData.PBOCData = string.IsNullOrEmpty(fiveAData) ? pbocData : fiveAData + "|" + pbocData;
                return pbocIcData;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }
        #endregion

        #region 获取磁条数据
        /// <summary>
        /// 获取重组磁条数据
        /// </summary>
        /// <param name="originTrackDatas">原始的磁条数据</param>
        /// <param name="config">配置文件</param>
        /// <returns></returns>
        public string GetTrackData(string originTrackDatas, PbocFileConfig config)
        {
            try
            {
                string trackData = null;//最终的磁条结果
                
                //获取规则
                TrackRules[] rules=new TrackRules[3];
                rules=GetTrackRules(config.trackRules);

                string track1Data=null;
                string track2Data=null;
                string track3Data=null;

                string[] sperateTrackDatas = originTrackDatas.Split(config.trackSeparators, StringSplitOptions.None);//分割

                foreach (TrackRules rule in rules)
                {
                    switch (rule.TagName)
                    {
                        case "track1":
                            track1Data = commonUtil.SubString(sperateTrackDatas[rule.column], rule.subStart, rule.subEnd);
                            break;
                        case "track2":
                            track2Data = commonUtil.SubString(sperateTrackDatas[rule.column], rule.subStart, rule.subEnd);
                            break;
                        case "track3":
                            track3Data = commonUtil.SubString(sperateTrackDatas[rule.column], rule.subStart, rule.subEnd);
                            break;                    
                        default:
                            break;
                    }           
                }

                trackData = string.Format("%{0}?;{1}?_;{2}?", track1Data, track2Data, track3Data);//拼成指定格式

                return trackData;
            }
            catch (Exception e)
            {
                throw new Exception("获取磁条数据出错，请检查配置！Error Messages:"+e.Message);
            }
        }

        struct TrackRules{
            public string TagName{set;get;}
            public int column{set;get;}
            public int subStart{set;get;}
            public int subEnd{set;get;}
        }
        private TrackRules[] GetTrackRules(string[] _rules)
        {
            TrackRules[] rules=new TrackRules[3];

            for (int i = 0; i < _rules.Length; i++)
            {
                string[] sperateRules=_rules[i].Split('|');
                rules[i].TagName = sperateRules[0].Trim();
                rules[i].column = int.Parse(sperateRules[1].Trim())-1;

                string[] sperateSubRules=sperateRules[2].Split(',');
                rules[i].subStart = int.Parse(sperateSubRules[0]);
                rules[i].subEnd = int.Parse(sperateSubRules[1]);               
            }
            return rules;
        }
        #endregion 

        #region 获取5A,bankcardId，personId
        /// <summary>
        /// 获取5A，银行卡号，身份证号
        /// </summary>
        /// <param name="fs">字符流</param>
        /// <param name="rules">规则</param>
        /// <param name="format">格式</param>
        /// <param name="isRevers">长度是否逆序</param>
        /// <returns>最终结果</returns>
        public string GetMatchMessges(Stream fs, string[] rules, int format, int isRevers)
        {
            long currentFsPosition = fs.Position;//当前文件流位置
            string DgiTag = null;//DGI的名字
            string originDatas = null;
            try
            {
                foreach (string rule in rules)
                {
                    Rule eachrule = GetRule(rule);
                    DgiTag = eachrule.DgiTag;
                    switch (eachrule.DgiTag)
                    {
                        case "over"://表示跳过的字节
                            {  
                                if (eachrule.mode == 2)
                                    fs.Position += eachrule.overStepSize;
                                else
                                    fs.Position += GetLength(fs, eachrule.lengthStart, eachrule.lengthEnd, eachrule.lengthConstantValue, isRevers) + eachrule.lengthEnd;
                            }
                            break;
                        case "bankCardId"://银行卡号
                        case "personId"://证件号
                        case "5A":    //银行卡号
                                originDatas = this.GetString(fs, eachrule, isRevers, format, true);
                            break;
                        default:
                            break;
                    }
                }
                return originDatas;
            }
            catch (Exception e)
            {
                throw new Exception(string.Format("执行getMatchMessges函数解析获取‘{0}’的数据时候出现异常执行\nerror:{1}",DgiTag,e.Message));
            }
            finally
            {
                fs.Position = currentFsPosition;
            }
        }
        #endregion

        #region 基础函数
        /// <summary>
        /// 获取每个参数规则
        /// </summary>
        /// <param name="eachRule">参数规则字符串</param>
        /// <returns>参数规则类rules</returns>
        public Rule GetRule(string eachRule)
        {
            Rule rule = new Rule();
            try
            {
                //变量
                string[] lenArray = null;
                string[] subArray = null;
                int temp = 0;         //临时

                int lengthStart = 0;  //长度开始位置
                int lengthEnd = 0;    //长度结束位置
                int constantLength = 0;//固定值

                int overStepSize = 0; //跳过开头多少
                int subSize = 0;      //截取的长度

                int mode = 2;

                //分割规则字符串
                string[] ruleSplits = eachRule.Split('|');

                //获取每个参数
                string stringDGITag = ruleSplits[0].Trim();

                //获取长度参数
                if (ruleSplits[1] != "")
                {
                    mode = 1;
                    lenArray = ruleSplits[1].Split(',');

                    if (lenArray.Length == 1)
                    {
                        temp = int.Parse(lenArray[0]);
                        if (temp < 0)
                            constantLength = -temp;
                        else
                        {
                            lengthStart = temp;
                            lengthEnd = lengthStart;
                        }
                    }
                    else
                    {
                        lengthStart = int.Parse(lenArray[0]);
                        lengthEnd = int.Parse(lenArray[1]);
                    }
                }

                //获取截取参数
                if (ruleSplits[2] != "")
                {
                    subArray = ruleSplits[2].Split(',');
                    overStepSize = int.Parse(subArray[0]);
                    subSize = subArray.Length == 1 ? 0 : int.Parse(subArray[1]);
                }
                //赋值每个参数
                rule.DgiTag = stringDGITag;

                rule.lengthStart = lengthStart;
                rule.lengthEnd = lengthEnd;
                rule.lengthConstantValue = constantLength;

                rule.overStepSize = overStepSize;
                rule.subSize = subSize;
                rule.mode = mode;
            }
            catch (Exception)
            {
                throw new Exception(eachRule + "参数获取失败！");
            }
            return rule;
        }
        /// <summary>
        /// 将byte[] 转为int
        /// </summary>
        /// <param name="byteArray">byte[]</param>
        /// <param name="isRevers">是否逆序,1逆序，0不逆序</param>
        /// <returns>int</returns>
        private int ByteArrayToInt(byte[] byteArray, int isRevers)
        {
            int result = 0;
            if (isRevers!=1)//正序
            {
                for (int i = 0, j = byteArray.Length - 1; i < byteArray.Length; )
                    result += (byteArray[i++] << (j--) * 8);
            }
            else//逆序
            {
                for (int i = byteArray.Length - 1; i >= 0; i--)
                    result += byteArray[i] << i * 8;
            }
            return result;
        }
        /// <summary>
        /// 获取数据中的长度
        /// </summary>
        /// <param name="fs">文件流指针</param>
        /// <param name="lengthStart">长度所在起始位置</param>
        /// <param name="lengthEnd">长度所在结束位置</param>
        /// <param name="isRevers">表示长度的字节是否逆序</param>
        /// <returns>长度</returns>
        public int GetLength(Stream fs, int lengthStart, int lengthEnd, int constantValue, int isRevers)
        {

            try
            {
                //变量
                int finalLength = 0;//最终的结果
                int bufferLength = lengthStart + lengthEnd - lengthStart;
                byte[] buffer = new byte[bufferLength];//存放长度字节
                byte[] lenbyte = null;//存放获取到的长度

                if (lengthStart == 0 && lengthStart == 0)
                    return constantValue
                        ;
                //读取
                fs.Read(buffer, 0, lengthEnd);
                fs.Position -= lengthEnd;

                //根据参数转存获取
                lenbyte = new byte[lengthEnd - lengthStart + 1];
                Array.Copy(buffer, lengthStart - 1, lenbyte, 0, lengthEnd - lengthStart + 1);
                finalLength = ByteArrayToInt(lenbyte, isRevers);

                return finalLength;
            }
            catch (Exception)
            {
                throw new Exception("在获取数据中的长度时出现异常！函数：getLength");
            }

        }

        /// <summary>
        /// 从fs当前文件流位置根据eachrule参数截取指定字符串
        /// </summary>
        /// <param name="fs">文件流</param>
        /// <param name="eachrule">截取的参数</param>
        /// <param name="isRevers">长度表示是否逆序</param>
        /// <param name="icDataFormat">获取的格式</param>
        /// <param name="whetherResetPositon">获取数据后指针是否复位</param>
        /// <returns></returns>
        public string GetString(Stream fs, Rule eachrule, int isRevers, int icDataFormat, bool whetherResetPositon)
        {
            long currentFsPosition = fs.Position;
            try
            {
                //获取长度
                int dataLen = GetLength(fs, eachrule.lengthStart, eachrule.lengthEnd, eachrule.lengthConstantValue, isRevers);

                //读取
                byte[] result = new byte[dataLen + eachrule.lengthEnd];
                fs.Read(result, 0, dataLen + eachrule.lengthEnd);
                string finalString = null;

                //截取
                switch (icDataFormat)
                {
                    case 0:
                        {
                            if (eachrule.subSize == 0)
                            {
                                finalString = BitConverter.ToString(result).Replace("-", "");//未截取前的数据
                                finalString = finalString.Substring(eachrule.overStepSize * 2);//未截取后的数据
                            }
                            else
                            {
                                finalString = BitConverter.ToString(result).Replace("-", "");//未截取前的数据
                                finalString = finalString.Substring(eachrule.overStepSize * 2, eachrule.subSize);//未截取后的数据
                            }
                        }
                        break;
                    case 1:
                        {
                            if (eachrule.subSize == 0)
                            {
                                finalString = Encoding.GetEncoding("gb2312").GetString(result).Replace("-", "");//未截取前的数据
                                finalString = finalString.Substring(eachrule.overStepSize);//未截取后的数据
                            }
                            else
                            {
                                finalString = Encoding.GetEncoding("gb2312").GetString(result).Replace("-", "");//未截取前的数据
                                finalString = finalString.Substring(eachrule.overStepSize, eachrule.subSize);//未截取后的数据
                            }

                        }
                        break;
                    default:
                        break;
                }
                return finalString;

            }
            catch (Exception)
            {
                throw new Exception(string.Format("执行函数getString获取{0}的数据时出现异常！",eachrule.DgiTag));
            }
            finally
            {
                if (whetherResetPositon)
                    fs.Position = currentFsPosition;// 指针复位
            }

        }
        #endregion
    }
    #region 结构体
    /// <summary>
    /// 获取每个DGI分组的规则参数
    /// </summary>
    public class Rule
    {
        /// <summary>
        /// mode为1表示变长长度,2表固定长度，
        /// </summary>
        public int mode { set; get; }

        /// <summary>
        /// DGI的名字
        /// </summary>
        public string DgiTag { set; get; }

        /// <summary>
        /// 表示长度的起始位置
        /// </summary>
        public int lengthStart { set; get; }

        /// <summary>
        /// 表示长度的结束位置
        /// </summary>
        public int lengthEnd { set; get; }

        /// <summary>
        /// 表示长度直接给定值
        /// </summary>
        public int lengthConstantValue { set; get; }

        /// <summary>
        /// 跳过开头第几个开始截取
        /// </summary>
        public int overStepSize { set; get; }
        /// <summary>
        /// 截取的长度
        /// </summary>
        public int subSize { set; get; }
    }
    #endregion
}
